package online.chazi.netkit;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelId;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import online.chazi.netkit.channel.NetkitFuture;
import online.chazi.netkit.messaging.Message;
import online.chazi.netkit.messaging.MessageDispatcher;
import online.chazi.netkit.messaging.MessageSendable;
import online.chazi.netkit.messaging.MessageTask;
import online.chazi.netkit.exception.SessionException;
import online.chazi.netkit.listener.DefaultSessionListener;
import online.chazi.netkit.listener.NetkitSessionListener;

import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;

/**
 * 说明：作者很懒，什么都没留下
 * Created by 叉子同学 on 2020-04-23 18:09
 */
@Slf4j
@Data
public class Session implements MessageSendable {

    private final MessageDispatcher dispatcher = new MessageDispatcher();

    private NetkitContext context;
    private Channel channel;
    private NetkitSessionListener sessionListener = new DefaultSessionListener();
    private long lastCommunicationTime = System.currentTimeMillis();

    private final Map<String, Object> attributeMap = new HashMap<String, Object>();

    public void setAttribute(String key, Object val) {
        this.attributeMap.put(key, val);
    }

    public Object getAttribute(String key) {
        return this.attributeMap.get(key);
    }

    public ChannelId getSessionId() {
        return channel.id();
    }

    public Session(NetkitContext netkitContext, Channel channel) {
        this.context = netkitContext;
        this.channel = channel;
        this.channel.closeFuture().addListener((ChannelFutureListener) channelFuture -> Session.this.onClosed());
    }

    public NetkitFuture sendMessageAndClose(Message message) {
        ChannelFuture future = sendMessage0(message);
        future.addListener(ChannelFutureListener.CLOSE);
        return new NetkitFuture(this, future);
    }

    public NetkitFuture sendMessage(Message message) {
        ChannelFuture future = sendMessage0(message);
        return new NetkitFuture(this, future);
    }

    private ChannelFuture sendMessage0(Message message) {
        if (!channel.isOpen()) {
            throw new SessionException("The channel was closed,cannot write message.");
        }
        return channel.writeAndFlush(context.getMessageEncrypt().encode(message));
    }

    public void onMessageReceived(Message message) {
        this.lastCommunicationTime = System.currentTimeMillis();
        MessageTask task = MessageTask.newInstance(dispatcher, this, context.getMessageEncrypt().decode(message));
        this.getContext().getWorkerExecutor().submit(task);
    }

    public NetkitFuture close() {
        ChannelFuture future = channel.close();
        return new NetkitFuture(this, future);
    }

    public Channel getChannel() {
        return channel;
    }

    protected void onCreated() {
        try {
            sessionListener.onCreated(this);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    protected void onClosed() {
        try {
            sessionListener.onClosed(this);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        attributeMap.clear();
    }

    public SocketAddress getRemoteAddress() {
        return this.channel.remoteAddress();
    }

    public boolean isClosed() {
        return !this.channel.isOpen();
    }

}
